package eval

import (
	"showen/lex"
	"showen/obj"
	"showen/parse"
	"testing"
)

func TestLambda(t *testing.T) {
	tests := []struct {
		input    string
		expected string
	}{
		{
			"((lambda (x) x) 5)",
			"5",
		},
		{
			"((lambda (x y) (+ x y)) 3 4)",
			"7",
		},
		{
			"((lambda (x) (+ x 1)) 5)",
			"6",
		},
		{
			"((lambda (x) ((lambda (y) (+ x y)) 2)) 3)",
			"5",
		},
		{
			"(define add (lambda (x y) (+ x y))) (add 3 4)",
			"7",
		},
		{
			"(define make-adder (lambda (x) (lambda (y) (+ x y)))) ((make-adder 3) 4)",
			"7",
		},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		exprs, err := parser.ParseProgram()
		if err != nil {
			t.Fatalf("parser.ParseProgram() error = %v", err)
		}

		var result obj.Object
		for _, expr := range exprs {
			result, err = EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("EvalExpression() error = %v", err)
			}
		}

		if result.Inspect() != tt.expected {
			t.Errorf("Eval() = %v, want %v", result.Inspect(), tt.expected)
		}
	}
}

func TestLambdaObject(t *testing.T) {
	input := "(lambda (x) (+ x 1))"
	lexer := lex.NewLexer(input)
	tokens := lexer.ReadTokens()
	parser := parse.NewParser(tokens)
	expr, err := parser.ParseExpression()
	if err != nil {
		t.Fatalf("parser.ParseExpression() error = %v", err)
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	result, err := EvalExpression(expr, env)
	if err != nil {
		t.Fatalf("EvalExpression() error = %v", err)
	}

	if result.Type() != obj.LAMBDA_OBJ {
		t.Errorf("Expected LAMBDA_OBJ, got %v", result.Type())
	}
}